﻿using QuanLyGiaoVuDAL.DTO;
using QuanLyGiaoVuService.Domain;
using QuanLyGiaoVuService.ReportServices;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace QuanLyGiaoVuService.CommonServices
{
    public class XepLichService : IXepLichService
    {
        private IDangKyGioDayReportService _dangKyGioDayReportService;
        private ITietHocService _tietHocService;
        private List<XepLichItemObject> _xepLichItemList;

        public XepLichService(IDangKyGioDayReportService dangKyGioDayReportService, ITietHocService tietHocService)
        {
            _dangKyGioDayReportService = dangKyGioDayReportService;
            _tietHocService = tietHocService;
        }

        public TimeBoard XepLich2(IEnumerable<DangKyGioDayDTO> dangKyGioDayDTOList)
        {
            _xepLichItemList = new List<XepLichItemObject>();
            foreach (DangKyGioDayDTO dangKyGioDayDTO in dangKyGioDayDTOList)
            {
                var thuTuTrongTuan = _tietHocService.GetThuTuTietTrongTuan(dangKyGioDayDTO.Thu, dangKyGioDayDTO.TietDauDTO.TietThu);
                _xepLichItemList.Add(new XepLichItemObject(dangKyGioDayDTO, thuTuTrongTuan));
            }

            List<XepLichState> openCollection = new List<XepLichState>();
            List<XepLichState> closeCollection = new List<XepLichState>();
            TimeBoard ketQua = new TimeBoard();

            TimeBoard firstTimeBoard = new TimeBoard();
            firstTimeBoard.XepLichItemList = _xepLichItemList;
            int thuTu = 0;
            firstTimeBoard.InitList();

            int firstG = firstTimeBoard.GetG();
            int firstH = firstTimeBoard.GetH();

            XepLichState firstState = new XepLichState(firstG, firstH, firstG + firstH, thuTu, firstTimeBoard, null);

            openCollection.Add(firstState);

            while (openCollection.Count > 0)
            {
                var minF = openCollection.Min(state => state.F);
                var currentState = openCollection.Find(state => state.F == minF);

                if (currentState.H == 0)
                {
                    ketQua = currentState.CurrentContent;
                    break;
                }

                int nextThuTu = currentState.CurrentContent.GetNextThuTu(currentState.ThuTu);
                foreach (var xepLichItem in currentState.CurrentContent.XepLichItemList)
                {
                    if (xepLichItem.DaXet == false && xepLichItem.ThuTu == nextThuTu)
                    {
                        XepLichState nextState = new XepLichState();
                        nextState.CurrentContent = new TimeBoard();
                        nextState.CurrentContent = currentState.CurrentContent.Chon(xepLichItem);
                        nextState.H = nextState.CurrentContent.GetH();
                        nextState.G = currentState.G + nextState.CurrentContent.GetG();
                        nextState.F = nextState.G + nextState.H;
                        nextState.ParentState = currentState;
                        nextState.ThuTu = nextThuTu;
                        openCollection.Add(nextState);
                    }
                }
                closeCollection.Add(currentState);
                openCollection.Remove(currentState);
            }
            if (ketQua.XepLichItemList.Count() > 0)
                return ketQua;
            else
            {
                var closestResult = ketQua;
                var closestResultList = closeCollection.Where(state => state.H == closeCollection.Min(state2 => state2.H));
                if (closestResultList.Count() > 0)
                {
                    closestResult = closestResultList.First().CurrentContent;
                }
                return closestResult;
            }
        }

        public TimeBoard XepLich(IEnumerable<DangKyGioDayDTO> dangKyGioDayDTOList)
        {
            _xepLichItemList = new List<XepLichItemObject>();
            foreach (DangKyGioDayDTO dangKyGioDayDTO in dangKyGioDayDTOList)
            {
                var thuTuTrongTuan = _tietHocService.GetThuTuTietTrongTuan(dangKyGioDayDTO.Thu, dangKyGioDayDTO.TietDauDTO.TietThu);
                _xepLichItemList.Add(new XepLichItemObject(dangKyGioDayDTO, thuTuTrongTuan));
            }

            List<XepLichState> openCollection = new List<XepLichState>();
            List<XepLichState> closeCollection = new List<XepLichState>();
            TimeBoard ketQua = new TimeBoard();

            TimeBoard firstTimeBoard = new TimeBoard();
            firstTimeBoard.XepLichItemList = _xepLichItemList;
            int thuTu = 0;
            firstTimeBoard.InitList();

            int firstG = firstTimeBoard.GetG();
            int firstH = firstTimeBoard.GetH();

            XepLichState firstState = new XepLichState(firstG, firstH, firstG + firstH, thuTu, firstTimeBoard, null);

            openCollection.Add(firstState);

            while (openCollection.Count > 0)
            {
                var minF = openCollection.Min(state => state.F);
                var currentState = openCollection.Find(state => state.F == minF);

                if (currentState.H == 0)
                {
                    ketQua = currentState.CurrentContent;
                    break;
                }

                var danhSachMaLopChuaXet = currentState.CurrentContent.LayDanhSachMaLopChuaXet();
                foreach (var maLop in danhSachMaLopChuaXet)
                {
                    XepLichItemObject phuongAnCaoNhat = currentState.CurrentContent.LayPhuongAnCaoNhatCuaLop(maLop);
                    if (phuongAnCaoNhat == null)
                        continue;
                    XepLichState nextState = new XepLichState();
                    nextState.CurrentContent = new TimeBoard();
                    nextState.CurrentContent = currentState.CurrentContent.Chon(phuongAnCaoNhat);
                    nextState.H = nextState.CurrentContent.GetH();
                    nextState.G = currentState.G + nextState.CurrentContent.GetG();
                    nextState.F = nextState.G + nextState.H;
                    nextState.ParentState = currentState;
                    nextState.LopHienTai = phuongAnCaoNhat.TenLop;
                     
                    openCollection.Add(nextState);
                }
                closeCollection.Add(currentState);
                openCollection.Remove(currentState);
            }
            if (ketQua.XepLichItemList.Count() > 0)
                return ketQua;
            else
            {
                var closestResult = ketQua;
                var closestResultList = closeCollection.Where(state => state.H == closeCollection.Min(state2 => state2.H));
                if (closestResultList.Count() > 0)
                {
                    closestResult = closestResultList.First().CurrentContent;
                }
                return closestResult;
            }
        }


        public Guid[] TimDangKyTrung(Guid MaDangKyGioDay)
        {
            List<Guid> dsIdTrung = new List<Guid>();
            DangKyGioDayDTO dangKyGioDayDTO = _dangKyGioDayReportService.GetThongTinDangKyGioDayById(MaDangKyGioDay);
            IEnumerable<DangKyGioDayDTO> dsDangKyGioDay = _dangKyGioDayReportService.GetAllThongTinDangKyGioDay(null, null, dangKyGioDayDTO.MaTietDau, dangKyGioDayDTO.MaTietSau, dangKyGioDayDTO.Thu,dangKyGioDayDTO.MaHocKy);
            foreach (DangKyGioDayDTO dk in dsDangKyGioDay)
            {
                if (dk.LopMonHocDTO.LopSinhVienId.Equals(dangKyGioDayDTO.LopMonHocDTO.LopSinhVienId) || dk.LopMonHocDTO.MaGiangVien1.Equals(dangKyGioDayDTO.LopMonHocDTO.MaGiangVien1))
                    dsIdTrung.Add(dk.MaThongTinGiangDay);
            }
            return dsIdTrung.ToArray();
        }
    }
}